import base64
import hashlib
import hmac
import json
import os
import logging
from datetime import datetime
from time import mktime
from urllib.parse import urlencode
from wsgiref.handlers import format_date_time
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests

# 配置日志（详细记录音频处理和API调用）
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[logging.FileHandler("voiceprint.log"), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

app = Flask(__name__)
CORS(app)  # 解决跨域问题


# 配置管理（请替换为你的科大讯飞API信息）
class Config:
    def __init__(self):
        self.app_id = os.getenv('APP_ID', '84b3457f')  # 替换为实际APP_ID
        self.api_key = os.getenv('API_KEY', '02540812ce357bed3742c6664414df9b')  # 替换为实际API_KEY
        self.api_secret = os.getenv('API_SECRET', 'YWQwMDJlZGE4Y2ZlOTNiZTc4NjdmZTdl')  # 替换为实际API_SECRET
        self.default_group_id = "my_group_001"
        self.default_threshold = 0.8
        self.base_url = 'https://api.xf-yun.com/v1/private/s782b4996'
        self.audio_min_size = 5 * 1024  # 最小音频大小（5KB）
        self.audio_max_size = 4 * 1024 * 1024  # 最大音频大小（4MB）


config = Config()


# 签名生成类（严格遵循API文档）
class GenReqUrl:
    def parse_url(self, request_url):
        try:
            stidx = request_url.index("://")
            host = request_url[stidx + 3:]
            edidx = host.index("/")
            if edidx <= 0:
                raise ValueError(f"无效的请求URL: {request_url}")
            self.path = host[edidx:]
            self.host = host[:edidx]
        except ValueError as e:
            logger.error(f"URL解析失败: {e}")
            raise

    def assemble_ws_auth_url(self, request_url, api_key, api_secret, method="POST"):
        self.parse_url(request_url)
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        # 生成签名（参考API文档）
        signature_origin = f"host: {self.host}\ndate: {date}\n{method} {self.path} HTTP/1.1"
        signature_sha = hmac.new(
            api_secret.encode('utf-8'),
            signature_origin.encode('utf-8'),
            digestmod=hashlib.sha256
        ).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = (
            f'api_key="{api_key}", algorithm="hmac-sha256", '
            f'headers="host date request-line", signature="{signature_sha}"'
        )
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')

        values = {"host": self.host, "date": date, "authorization": authorization}
        return request_url + "?" + urlencode(values)


# 声纹识别核心类
class VoiceprintService:
    def __init__(self):
        self.url_generator = GenReqUrl()

    def send_request(self, api_name, file_bytes=None, feature_id=None, group_id=None):
        # 生成带签名的URL
        request_url = self.url_generator.assemble_ws_auth_url(
            request_url=config.base_url,
            api_key=config.api_key,
            api_secret=config.api_secret
        )

        # 构建请求体
        request_body = self._generate_request_body(
            api_name=api_name,
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id or config.default_group_id
        )

        headers = {'Content-Type': 'application/json', 'Host': 'api.xf-yun.com'}
        try:
            logger.debug(f"发送API请求: {api_name}, URL: {request_url}")
            response = requests.post(
                url=request_url,
                headers=headers,
                data=json.dumps(request_body),
                timeout=30
            )
            logger.debug(f"API响应状态: {response.status_code}, 内容: {response.text}")

            if response.status_code != 200:
                raise Exception(f"请求失败: {response.text}")

            response_json = response.json()
            # 检查API返回的错误码
            if response_json.get("header", {}).get("code") != 0:
                code = response_json["header"]["code"]
                message = response_json["header"]["message"]
                logger.error(f"API错误: 代码={code}, 信息={message}")
                raise Exception(
                    f'{{"header":{{"code":{code},"message":"{message}","sid":"{response_json["header"].get("sid")}"}}}}')

            return response_json
        except Exception as e:
            logger.error(f"请求错误: {str(e)}")
            raise

    def _generate_request_body(self, api_name, file_bytes=None, feature_id=None, group_id=None):
        base_body = {
            "header": {"app_id": config.app_id, "status": 3},
            "parameter": {"s782b4996": {"groupId": group_id}}
        }

        if api_name == "createGroup":
            base_body["parameter"]["s782b4996"].update({
                "func": "createGroup",
                "groupName": "声纹识别测试组",
                "groupInfo": "用于Web界面测试",
                "createGroupRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            return base_body

        elif api_name == "createFeature":
            if not file_bytes or not feature_id:
                raise ValueError("缺少音频文件或用户ID")
            # 验证音频大小
            if len(file_bytes) < config.audio_min_size:
                raise ValueError(f"音频过小（{len(file_bytes)} bytes），请录制3-5秒语音")
            if len(file_bytes) > config.audio_max_size:
                raise ValueError(f"音频过大（{len(file_bytes)} bytes），最大支持4MB")

            base_body["parameter"]["s782b4996"].update({
                "func": "createFeature",
                "featureId": feature_id,
                "featureInfo": f"用户{feature_id}的声纹特征",
                "createFeatureRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            base_body["payload"] = {
                "resource": {
                    "encoding": "lame",  # 明确指定MP3编码方式为lame（API要求）
                    "sample_rate": 16000,  # 强制16kHz
                    "channels": 1,  # 强制单声道
                    "bit_depth": 16,  # 强制16bit
                    "status": 3,
                    "audio": base64.b64encode(file_bytes).decode('UTF-8')
                }
            }
            return base_body

        elif api_name == "searchScoreFea":
            if not file_bytes or not feature_id:
                raise ValueError("缺少音频文件或用户ID")
            # 验证音频大小
            if len(file_bytes) < config.audio_min_size:
                raise ValueError(f"音频过小（{len(file_bytes)} bytes），请录制3-5秒语音")

            base_body["parameter"]["s782b4996"].update({
                "func": "searchScoreFea",
                "dstFeatureId": feature_id,
                "searchScoreFeaRes": {"encoding": "utf8", "compress": "raw", "format": "json"}
            })
            base_body["payload"] = {
                "resource": {
                    "encoding": "lame",
                    "sample_rate": 16000,
                    "channels": 1,
                    "bit_depth": 16,
                    "status": 3,
                    "audio": base64.b64encode(file_bytes).decode('UTF-8')
                }
            }
            return base_body

        else:
            raise ValueError(f"不支持的API: {api_name}")

    def decode_response(self, response, field_name):
        try:
            encoded_text = response.get("payload", {}).get(field_name, {}).get("text", "")
            return json.loads(base64.b64decode(encoded_text).decode('utf-8')) if encoded_text else None
        except Exception as e:
            logger.error(f"响应解码失败: {str(e)}")
            return None


# 初始化服务
voiceprint_service = VoiceprintService()


# Flask路由
@app.route('/api/create-group', methods=['POST'])
def create_group():
    try:
        data = request.json
        group_id = data.get('group_id', config.default_group_id)
        logger.info(f"创建分组请求: {group_id}")

        response = voiceprint_service.send_request("createGroup", group_id=group_id)

        if response["header"]["code"] == 0:
            return jsonify({"status": "success", "message": f"分组 {group_id} 创建成功"})
        elif response["header"]["code"] == 23006:
            return jsonify({"status": "warning", "message": f"分组 {group_id} 已存在"})
        else:
            return jsonify({"status": "error", "message": response["header"]["message"]})
    except Exception as e:
        logger.exception("创建分组失败")
        return jsonify({"status": "error", "message": str(e)}), 500


@app.route('/api/register-feature', methods=['POST'])
def register_feature():
    try:
        feature_id = request.form.get('feature_id')
        group_id = request.form.get('group_id', config.default_group_id)
        audio_file = request.files.get('audio')

        # 后端参数校验
        if not audio_file or not feature_id:
            return jsonify({"status": "error", "message": "请提供音频文件和用户ID"}), 400

        file_bytes = audio_file.read()
        logger.info(f"收到注册请求: feature_id={feature_id}, group_id={group_id}, 音频大小={len(file_bytes)} bytes")

        # 调用声纹识别服务
        response = voiceprint_service.send_request(
            "createFeature",
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id
        )

        return jsonify({"status": "success", "message": f"用户 {feature_id} 声纹注册成功"})
    except Exception as e:
        logger.exception("注册声纹特征失败")
        return jsonify({"status": "error", "message": str(e)}), 500


@app.route('/api/verify-feature', methods=['POST'])
def verify_feature():
    try:
        feature_id = request.form.get('feature_id')
        group_id = request.form.get('group_id', config.default_group_id)
        threshold = float(request.form.get('threshold', config.default_threshold))
        audio_file = request.files.get('audio')

        # 后端参数校验
        if not audio_file or not feature_id:
            return jsonify({"status": "error", "message": "请提供音频文件和用户ID"}), 400

        file_bytes = audio_file.read()
        logger.info(f"收到验证请求: feature_id={feature_id}, group_id={group_id}, 音频大小={len(file_bytes)} bytes")

        # 调用声纹识别服务
        response = voiceprint_service.send_request(
            "searchScoreFea",
            file_bytes=file_bytes,
            feature_id=feature_id,
            group_id=group_id
        )

        # 解析结果
        result_data = voiceprint_service.decode_response(response, "searchScoreFeaRes")
        score = result_data.get("score", 0)
        is_match = score >= threshold

        return jsonify({
            "status": "success",
            "score": round(score, 2),
            "threshold": threshold,
            "is_match": is_match
        })
    except Exception as e:
        logger.exception("验证声纹特征失败")
        return jsonify({"status": "error", "message": str(e)}), 500


if __name__ == '__main__':
    app.run(debug=True,port=5008)